package xj.model.uml;

import static xj.model.uml.UMLModelConstants.ATTR_ABSTRACT;
import static xj.model.uml.UMLModelConstants.ATTR_CONCURRENCY;
import static xj.model.uml.UMLModelConstants.ATTR_QUERY;
import static xj.model.uml.UMLModelConstants.ROLE_PARAMETER;
import static xj.model.uml.UMLModelConstants.ROLE_ACTION;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class Operation extends Feature {

  public enum Concurrency {
    Sequential, Concurrent, Guarded
  };

  public Operation() {
    this(null, null);
  }

  public Operation(String name, String type) {
    super(name, type);
    destructable = false;
    preserved = true;
  }

  public Operation(String name, String type, Visibility visibility) {
    super(name, type, visibility);
    destructable = false;
    preserved = true;
  }

  @Override
  public Object clone() throws CloneNotSupportedException {
    Operation oper = (Operation) super.clone();
    oper.setAbstract(abstract_);
    oper.setQuery(query);
    oper.setConcurrency(concurrency);

    oper.parameters = new ArrayList<Parameter>();
    Iterator<Parameter> iter = parameters.iterator();
    while (iter.hasNext()) {
      Parameter p = iter.next();
      oper.addParameter((Parameter) p.clone());
    }

    if (action != null) { 
      oper.action = (Action) action.clone();
    }

    return oper;
  }

  public boolean isAbstract() {
    return abstract_;
  }

  public void setAbstract(boolean s) {
    abstract_ = s;
    setAttribute(ATTR_ABSTRACT, s);
  }

  public boolean isQuery() {
    return query;
  }

  public void setQuery(boolean s) {
    query = s;
    setAttribute(ATTR_QUERY, s);
  }

  public List<Parameter> getParameters() {
    return parameters;
  }

  public Concurrency getConcurrency() {
    return concurrency;
  }

  public void setConcurrency(Concurrency concurrency) {
    this.concurrency = concurrency;
    setAttribute(ATTR_CONCURRENCY, concurrency);
  }

  public String getParametersString() {
    StringBuilder sb = new StringBuilder();
    sb.append('(');
    if (parameters != null) {
      boolean first = true;
      Iterator<Parameter> iter = parameters.iterator();
      while (iter.hasNext()) {
	Parameter p = iter.next();
	if (first) {
	  first = false;
	} else {
	  sb.append(", ");
	}
	sb.append(p.toString());
      }
    }
    sb.append(')');
    return sb.toString();
  }

  public void addParameter(Parameter p) {
    if (p != null) {
      parameters.add(p);
      addOwnedElement(p, ROLE_PARAMETER);
    }
  }

  public void removeParameter(Parameter p) {
    if (p != null) {
      parameters.remove(p);
      removeOwnedElement(p);
    }
  }

  public void setParameters(List<Parameter> params) {
    removeAllParameters();
    if (params != null) {
      int n = params.size();
      if (n > 0) {
	for (int i = 0; i < n; i++) {
	  Parameter p = params.get(i);
	  addParameter(p);
	}
      }
    }
  }

  public void setParameters(String params) {
    if (params != null) {
      List<Parameter> pl = Parameter.makeParameters(null, params);
      setParameters(pl);
    } else {
      removeAllParameters();
    }
  }

  public void updateParameters(String params) {
    if (params != null) {
      List<Parameter> pl = Parameter.makeParameters(parameters, params);
      setParameters(pl);
    } else {
      removeAllParameters();
    }
  }

  public void removeAllParameters() {
    Iterator<Parameter> iter = parameters.iterator();
    while (iter.hasNext()) {
      Parameter p = iter.next();
      removeOwnedElement(p);
      iter.remove();
    }

    /*
     * parameters.clear(); removeOwnedElements(ROLE_PARAMETER);
     */
  }

  public Action getAction() { 
    return action;
  }

  public void setAction(Action a) { 
    this.action = a;
    setOwnedElement(ROLE_ACTION, a);
  } 

  @Override
  public String toString() {
    return toString(false, false);
  }

  @Override
  public String toString(boolean withStereotype) {
    return toString(true, false);
  }

  public String toString(boolean withStereotype, boolean withProperties) {
    StringBuilder sb = new StringBuilder();
    if (withStereotype) {
      Set<String> st = getStereotypes();
      if (st != null && st.size() > 0) {
	sb.append("\u00AB").append(getStereotypeString()).append("\u00BB ");
      }
    }

    Visibility vis = getVisibility();
    if (vis != null && 
	vis != Visibility.Default) { 
      sb.append(vis.toChar()).append(' ');
    }
    sb.append(name);
    sb.append(getParametersString());
    if (type != null) {
      sb.append(": " + type);
    }

    if (withProperties) {
      String prop = getPropertiesString();
      if (prop != null) {
	sb.append(" ").append(prop);
      }
    }
    return sb.toString();
  }

  public String getPropertiesString() {
    StringBuilder sb = new StringBuilder();
    Set<String> pset = new HashSet<String>();
    if (query) {
      pset.add("query");
    }

    if (concurrency != null) {
      pset.add(concurrency.toString());
    }

    if (pset.size() > 0) {
      boolean first = true;
      Iterator<String> pi = pset.iterator();
      sb.append("{");
      while (pi.hasNext()) {
	if (first) {
	  first = false;
	} else {
	  sb.append(", ");
	}
	sb.append(pi.next());
      }
      sb.append("}");
    }
    if (sb.length() > 0) {
      return sb.toString();
    } else {
      return null;
    }
  }

  public static Operation makeOperation(String ostring) {
    return makeOperation(null, ostring);
  }

  public static Operation makeOperation(Operation operation, String ostring) {
    if (ostring != null) {
      if (operation == null) {
	operation = new Operation();
      }

      ostring = ostring.trim();
      int i = 0;
      if (ostring.length() > 0) {
	char c = ostring.charAt(0);
	if (c == '\u00AB') {
	  i = ostring.indexOf('\u00BB');
	  if (i > 1) {
	    String st = ostring.substring(1, i);
	    operation.setStereotypes(st);
	  } else {
	    i = 0;
	  }
	  ostring = ostring.substring(i + 1).trim();
	  c = ostring.charAt(0);
	  i = 0;
	}

	Visibility v = Visibility.Default;
	switch (c) {
	case '+':
	  v = Visibility.Public;
	  i = 1;
	  break;

	case '-':
	  v = Visibility.Private;
	  i = 1;
	  break;

	case '#':
	  v = Visibility.Protected;
	  i = 1;
	  break;

	case '~':
	  v = Visibility.Package;
	  i = 1;
	  break;
	default:
	}
	operation.setVisibility(v);
	if (i > 0) {
	  ostring = ostring.substring(i).trim();
	}
	i = ostring.indexOf(':');
	if (i >= 0) {
	  operation.setType(ostring.substring(i + 1).trim());
	  ostring = ostring.substring(0, i).trim();
	}
	i = ostring.indexOf('(');
	if (i >= 0) {
	  operation.setName(ostring.substring(0, i).trim());
	  operation.updateParameters(ostring.substring(i).trim());
	} else {
	  operation.setName(ostring);
	}
      }
    }
    return operation;
  }

  protected List<Parameter> parameters = new ArrayList<Parameter>();

  protected Action action = null; 

  protected boolean abstract_ = false;

  protected boolean query = false;

  protected Concurrency concurrency = null; // Concurrency.Sequential;

}
